﻿// License Notification
//======================================================
// Copyright (c) 2010 Channel Intelligence Inc.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// ======================================================




using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Reflection;
using CIP4.Common;
using CIP4.Item;
using CIP4.Simple;

namespace CIP4.DataProvider.MassTransit
{
    public class DataProvider : CIP4.Simple.DataProvider, IDataProvider
    {
        private static Dictionary<string, ClientHandler> _handlers = new Dictionary<string, ClientHandler>();

        IDataProvider Cache { get; set; }

        public string ConnectionString { get; private set; }

        private readonly string _busId;

        private readonly ClientHandler _handler;

        public DataProvider(CIP4ConnectionAttribute dataProviderConnection, IDataProvider cachingProvider, LoggerDelegate logger)
            : base(dataProviderConnection)
        {
            Cache = cachingProvider;
            if (!(dataProviderConnection is CIP4MassTransitConnectionAttribute))
                LoggerWrapper(MajorEnum.Error, MinorEnum.UnexpectedConnectionType,
                    "{0} is not an acceptable connection type", new object[] { dataProviderConnection.ToString() }, logger);
            else
            {
                LoggerWrapper(MajorEnum.Trace, MinorEnum.InitializingProvider,
                    "Initalizing Provider: {0}", new object[] { dataProviderConnection }, logger);
                CIP4MassTransitConnectionAttribute connection = (CIP4MassTransitConnectionAttribute)dataProviderConnection;
                ConnectionString = connection.ToString();
                _busId = connection.BusId;
                if (_handlers.ContainsKey(_busId))
                {
                    _handler = _handlers[_busId];
                }
                else
                {
                    _handler = new ClientHandler(logger, _busId, connection.TimeOutInMilliSecs);
                    _handlers.Add(_busId, _handler);                    
                }
            }
        }

        #region IDataProvider Members

        public override IEnumerable<object> GetItems(CollectionType collection, IBooleanValue selectionExpression, CursorType cursor)
        {
            if (!LoggerWrapper(MajorEnum.Trace, MinorEnum.ActionStart,
                "Started {0}", new object[] { MinorEnum.GetItems }, cursor.Logger))
                return new List<object>();

            ISerializerType serializer = cursor.GetSerializer(SerializerKind);

            string lookForExternalId = GetExternalIdValueFromSelectionExpression(selectionExpression, serializer);

            if (Cache != null && !string.IsNullOrEmpty(lookForExternalId))
            {
                List<object> items = new List<object>();
                foreach (object item in Cache.GetItems(collection, selectionExpression, cursor))
                {
                    LoggerWrapper(MajorEnum.Trace, MinorEnum.CacheHit, "Cache hit: {0}", new[] { items[0] }, cursor.Logger);
                    items.Add(item);
                    return items;
                }
            }

            if (string.IsNullOrEmpty(lookForExternalId))
                if (cursor.FailOnLinearSearch)
                {
                    try
                    {
                        if (!LoggerWrapper(MajorEnum.Error, MinorEnum.FailOnLinearSearch,
                            "Failed on linear search accessing {0}", new object[] { collection }, cursor.Logger))
                            return new List<object>();
                    }
                    finally 
                    {
                        // this looks odd - but it reflects behavior by other providers which typically do the 
                        // "Get N items from..." message in a finally at the end of the GetItems function
                        LoggerWrapper(MajorEnum.Information, MinorEnum.GetItems,
                            "Get {0} items from {1}", new object[] { 0, collection }, cursor.Logger);
                    }
                    

                }

            return _handler.GetItems(collection, selectionExpression, cursor);
        }

        public void SaveItems(IEnumerable<object> items, CollectionType collection, GetSerializerDelegate getSerializer)
        {
            _handler.SaveItems(items, collection, getSerializer);

            if (Cache != null)
                Cache.SaveItems(items, collection, getSerializer);
        }

        public override void PurgeItems(IEnumerable<ExternalIDType> externalIDs, CollectionType collection, GetSerializerDelegate getSerializer)
        {
            _handler.PurgeItems(externalIDs, collection, getSerializer);

            // Potential provider discrepancy here as other providers log the PurgeItems completed message after the Cache.PurgeItems call
            if (Cache != null)
                Cache.PurgeItems(externalIDs, collection, getSerializer);
        }

        public void DeleteCollection(CollectionType collection, LoggerDelegate logger)
        {
            _handler.DeleteCollection(collection, logger);
            if (Cache != null)
                Cache.DeleteCollection(collection, logger);
        }

        public void Terminate(LoggerDelegate logger)
        {
        }

        #endregion
    }
}
